package cn.zglbk.utils;

import cn.zglbk.pb.BytesArray;
import com.google.protobuf.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;

/**
 * @author 猪哥亮
 * @email 1030681978@qq.com
 * @date 2020/6/21 17:26
 * 描述：
 */
public class SerializableUtils {
    private final static Logger log = LoggerFactory.getLogger(SerializableUtils.class);
    private static Map<Class, ParserAction> priParser = new HashMap<>();
    private static Map<Class, ToByteArrayAction> priToByteArray = new HashMap<>();
    private static Map<Class, Parser<MessageLite>> map = new HashMap<>();

    private static <M extends MessageLite> AbstractParser<M> doParser(Class<M> type) {
        if (map.containsKey(type)) {
            return (AbstractParser<M>) map.get(type);
        }
        AbstractParser<M> abstractParser = new AbstractParser<M>() {
            @Override
            public M parsePartialFrom(
                    CodedInputStream input,
                    ExtensionRegistryLite extensionRegistry) {
                try {
                    Constructor c = type.getDeclaredConstructor(CodedInputStream.class, ExtensionRegistryLite.class);
                    c.setAccessible(true);
                    M obj = (M) c.newInstance(input, extensionRegistry);
                    return obj;
                } catch (Exception e) {
                    log.error("序列化异常！",e);
                }
                return null;
            }
        };
        map.put(type, (Parser<MessageLite>) abstractParser);
        return abstractParser;
    }

    public static Object parser(Class type, byte[] buff) throws InvalidProtocolBufferException {
        ParserAction action = priParser.getOrDefault(type, null);
        if (action != null) {
            return action.action(buff);
        }
        AbstractParser mParser = doParser(type);
        return mParser.parseFrom(buff);
    }

    public static byte[] toByteArray(com.google.protobuf.Message.Builder builder) {
        return builder.build().toByteArray();
    }
    public static byte[] toByteArray(Object builder) {
        ToByteArrayAction arrs = priToByteArray.getOrDefault(builder.getClass(), null);
        if(arrs != null){
            try {
                return arrs.action(builder);
            }catch (InvalidProtocolBufferException e){
                return null;
            }
        }
        return ((com.google.protobuf.Message.Builder)builder).build().toByteArray();
    }
    static {
        priParser.put(String.class, value -> {
            BytesArray.StringParam builder = BytesArray.StringParam.parseFrom(value);
            return builder.getValue();
        });
        priParser.put(int.class, value -> {
            BytesArray.IntParam builder = BytesArray.IntParam.parseFrom(value);
            return builder.getValue();
        });
        priParser.put(Integer.class,  value -> {
            BytesArray.IntParam builder = BytesArray.IntParam.parseFrom(value);
            return builder.getValue();
        });

        priParser.put(long.class,  value -> {
            BytesArray.LongParam builder = BytesArray.LongParam.parseFrom(value);
            return builder.getValue();
        });
        priParser.put(Long.class,value -> {
            BytesArray.LongParam builder = BytesArray.LongParam.parseFrom(value);
            return builder.getValue();
        });

        priParser.put(float.class, value -> {
            BytesArray.FloatParam builder = BytesArray.FloatParam.parseFrom(value);
            return builder.getValue();
        });
        priParser.put(Float.class, value -> {
            BytesArray.FloatParam builder = BytesArray.FloatParam.parseFrom(value);
            return builder.getValue();
        });

        priParser.put(double.class, value -> {
            BytesArray.DoubleParam builder = BytesArray.DoubleParam.parseFrom(value);
            return builder.getValue();
        });
        priParser.put(Double.class, value -> {
            BytesArray.DoubleParam builder = BytesArray.DoubleParam.parseFrom(value);
            return builder.getValue();
        });

        priParser.put(boolean.class, value -> {
            BytesArray.BooleanParam builder = BytesArray.BooleanParam.parseFrom(value);
            return builder.getValue();
        });
        priParser.put(Boolean.class,value -> {
            BytesArray.BooleanParam builder = BytesArray.BooleanParam.parseFrom(value);
            return builder.getValue();
        });

        priToByteArray.put(String.class, (ToByteArrayAction<String>) value -> {
            BytesArray.StringParam.Builder builder = BytesArray.StringParam.newBuilder();
            builder.setValue(value);
            return builder.build().toByteArray();
        });
        priToByteArray.put(int.class, (ToByteArrayAction<Integer>)value -> {
            BytesArray.IntParam.Builder builder = BytesArray.IntParam.newBuilder();
            builder.setValue(value);
            return builder.build().toByteArray();
        });
        priToByteArray.put(Integer.class,  (ToByteArrayAction<Integer>)value -> {
            BytesArray.IntParam.Builder builder = BytesArray.IntParam.newBuilder();
            builder.setValue(value);
            return builder.build().toByteArray();
        });

        priToByteArray.put(long.class, (ToByteArrayAction<Long>)value -> {
            BytesArray.LongParam.Builder builder = BytesArray.LongParam.newBuilder();
            builder.setValue(value);
            return builder.build().toByteArray();
        });
        priToByteArray.put(Long.class,(ToByteArrayAction<Long>)value -> {
            BytesArray.LongParam.Builder builder = BytesArray.LongParam.newBuilder();
            builder.setValue(value);
            return builder.build().toByteArray();
        });

        priToByteArray.put(float.class, (ToByteArrayAction<Float>)value -> {
            BytesArray.FloatParam.Builder builder = BytesArray.FloatParam.newBuilder();
            builder.setValue(value);
            return builder.build().toByteArray();
        });
        priToByteArray.put(Float.class, (ToByteArrayAction<Float>)value -> {
            BytesArray.FloatParam.Builder builder = BytesArray.FloatParam.newBuilder();
            builder.setValue(value);
            return builder.build().toByteArray();
        });

        priToByteArray.put(double.class, (ToByteArrayAction<Double>)value -> {
            BytesArray.DoubleParam.Builder builder = BytesArray.DoubleParam.newBuilder();
            builder.setValue(value);
            return builder.build().toByteArray();
        });
        priToByteArray.put(Double.class,(ToByteArrayAction<Double>)value -> {
            BytesArray.DoubleParam.Builder builder = BytesArray.DoubleParam.newBuilder();
            builder.setValue(value);
            return builder.build().toByteArray();
        });

        priToByteArray.put(boolean.class, (ToByteArrayAction<Boolean>)value -> {
            BytesArray.BooleanParam.Builder builder = BytesArray.BooleanParam.newBuilder();
            builder.setValue(value);
            return builder.build().toByteArray();
        });
        priToByteArray.put(Boolean.class,(ToByteArrayAction<Boolean>)value -> {
            BytesArray.BooleanParam.Builder builder = BytesArray.BooleanParam.newBuilder();
            builder.setValue(value);
            return builder.build().toByteArray();
        });
    }

    private interface ParserAction {
        /**
         * @return
         */
        Object action(byte[] value) throws InvalidProtocolBufferException;
    }
    private interface ToByteArrayAction<T> {
        /**
         * @return
         */
        byte[] action(T value) throws InvalidProtocolBufferException;
    }
}
